home *** CD-ROM | disk | FTP | other *** search
/ Aminet 48 / Aminet 48 (2002)(GTI - Schatztruhe)[!][Apr 2002].iso / Aminet / dev / src / expat-src.lha / expat-1.95.2 / xmlwf / wfcheck.c < prev    next >
Encoding:
C/C++ Source or Header  |  2001-07-28  |  22.9 KB  |  954 lines

  1. #include <stdlib.h>
  2. #include <string.h>
  3.  
  4. #include "wfcheck.h"
  5. #include "hashtable.h"
  6.  
  7. #include "xmltok.h"
  8. #include "xmlrole.h"
  9.  
  10. typedef struct {
  11.   const char *name;
  12.   const char *textPtr;
  13.   size_t textLen;
  14.   const char *docTextPtr;
  15.   const char *systemId;
  16.   const char *publicId;
  17.   const char *notation;
  18.   char open;
  19.   char wfInContent;
  20.   char wfInAttribute;
  21.   char magic;
  22. } ENTITY;
  23.  
  24. #define INIT_BLOCK_SIZE 1024
  25.  
  26. typedef struct block {
  27.   struct block *next;
  28.   char s[1];
  29. } BLOCK;
  30.  
  31. typedef struct {
  32.   BLOCK *blocks;
  33.   const char *end;
  34.   char *ptr;
  35.   char *start;
  36. } STRING_POOL;
  37.  
  38. typedef struct {
  39.   HASH_TABLE generalEntities;
  40.   HASH_TABLE paramEntities;
  41.   STRING_POOL pool;
  42.   int containsRef;
  43.   int standalone;
  44.   char *groupConnector;
  45.   size_t groupSize;
  46. } DTD;
  47.  
  48. typedef struct {
  49.   DTD dtd;
  50.   size_t stackSize;
  51.   const char **startName;
  52.   int attsSize;
  53.   ATTRIBUTE *atts;
  54. } CONTEXT;
  55.  
  56. static void poolInit(STRING_POOL *);
  57. static void poolDestroy(STRING_POOL *);
  58. static const char *poolAppend(STRING_POOL *pool, const ENCODING *enc,
  59.                   const char *ptr, const char *end);
  60. static const char *poolStoreString(STRING_POOL *pool, const ENCODING *enc,
  61.                 const char *ptr, const char *end);
  62. static int poolGrow(STRING_POOL *);
  63. static int dtdInit(DTD *);
  64. static void dtdDestroy(DTD *);
  65. static int contextInit(CONTEXT *);
  66. static void contextDestroy(CONTEXT *);
  67.  
  68. #define poolStart(pool) ((pool)->start)
  69. #define poolDiscard(pool) ((pool)->ptr = (pool)->start)
  70. #define poolFinish(pool) ((pool)->start = (pool)->ptr)
  71.  
  72. static enum WfCheckResult
  73. checkProlog(DTD *, const char *s, const char *end, const char **, const ENCODING **enc);
  74. static enum WfCheckResult
  75. checkContent(size_t level, CONTEXT *context, const ENCODING *enc,
  76.            const char *s, const char *end, const char **badPtr);
  77. static enum WfCheckResult
  78. checkGeneralTextEntity(CONTEXT *context,
  79.                const char *s, const char *end,
  80.                const char **nextPtr,
  81.                const ENCODING **enc);
  82. static enum WfCheckResult
  83. checkAttributeValue(DTD *, const ENCODING *, const char *, const char *, const char **);
  84. static enum WfCheckResult
  85. checkAttributeUniqueness(CONTEXT *context, const ENCODING *enc, int nAtts,
  86.              const char **badPtr);
  87. static enum WfCheckResult
  88. checkParsedEntities(CONTEXT *context, const char **badPtr);
  89.  
  90. static
  91. enum WfCheckResult storeEntity(DTD *dtd,
  92.                    const ENCODING *enc,
  93.                    int isParam,
  94.                    const char *entityNamePtr,
  95.                    const char *entityNameEnd,
  96.                    const char *entityTextPtr,
  97.                    const char *entityTextEnd,
  98.                    const char **badPtr);
  99.  
  100.  
  101. enum WfCheckResult
  102. wfCheck(enum EntityType entityType, const char *s, size_t n,
  103.     const char **badPtr, unsigned long *badLine, unsigned long *badCol)
  104. {
  105.   CONTEXT context;
  106.   const ENCODING *enc;
  107.   const char *start = s;
  108.   const char *end = s + n;
  109.   const char *next = 0;
  110.   enum WfCheckResult result;
  111.  
  112.   if (!contextInit(&context)) {
  113.     contextDestroy(&context);
  114.     return noMemory;
  115.   }
  116.   if (entityType == documentEntity) {
  117.     result = checkProlog(&context.dtd, s, end, &next, &enc);
  118.     s = next;
  119.     if (!result) {
  120.       result = checkParsedEntities(&context, &next);
  121.       s = next;
  122.       if (!result) {
  123.     result = checkContent(0, &context, enc, s, end, &next);
  124.     s = next;
  125.       }
  126.     }
  127.   }
  128.   else {
  129.     result = checkGeneralTextEntity(&context, s, end, &next, &enc);
  130.     s = next;
  131.   }
  132.   if (result && s) {
  133.     POSITION pos;
  134.     memset(&pos, 0, sizeof(POSITION));
  135.     XmlUpdatePosition(enc, start, s, &pos);
  136.     *badPtr = s;
  137.     *badLine = pos.lineNumber;
  138.     *badCol = pos.columnNumber;
  139.   }
  140.   contextDestroy(&context);
  141.   return result;
  142. }
  143.  
  144. static
  145. int contextInit(CONTEXT *p)
  146. {
  147.   p->stackSize = 1024;
  148.   p->startName = malloc(p->stackSize * sizeof(char *));
  149.   p->attsSize = 1024;
  150.   p->atts = malloc(p->attsSize * sizeof(ATTRIBUTE));
  151.   return dtdInit(&(p->dtd)) && p->atts && p->startName;
  152. }
  153.  
  154. static
  155. void contextDestroy(CONTEXT *p)
  156. {
  157.   dtdDestroy(&(p->dtd));
  158.   free((void *)p->startName);
  159.   free((void *)p->atts);
  160. }
  161.  
  162. static enum WfCheckResult
  163. checkContent(size_t level, CONTEXT *context, const ENCODING *enc,
  164.          const char *s, const char *end, const char **badPtr)
  165. {
  166.   size_t startLevel = level;
  167.   const char *next;
  168.   int tok = XmlContentTok(enc, s, end, &next);
  169.   for (;;) {
  170.     switch (tok) {
  171.     case XML_TOK_TRAILING_CR:
  172.     case XML_TOK_NONE:
  173.       if (startLevel > 0) {
  174.     if (level != startLevel) {
  175.       *badPtr = s;
  176.       return asyncEntity;
  177.         }
  178.     return wellFormed;
  179.       }
  180.       *badPtr = s;
  181.       return noElements;
  182.     case XML_TOK_INVALID:
  183.       *badPtr = next;
  184.       return invalidToken;
  185.     case XML_TOK_PARTIAL:
  186.       *badPtr = s;
  187.       return unclosedToken;
  188.     case XML_TOK_PARTIAL_CHAR:
  189.       *badPtr = s;
  190.       return partialChar;
  191.     case XML_TOK_EMPTY_ELEMENT_NO_ATTS:
  192.       break;
  193.     case XML_TOK_ENTITY_REF:
  194.       {
  195.     const char *name = poolStoreString(&context->dtd.pool, enc,
  196.                        s + enc->minBytesPerChar,
  197.                        next - enc->minBytesPerChar);
  198.     ENTITY *entity = (ENTITY *)lookup(&context->dtd.generalEntities, name, 0);
  199.     poolDiscard(&context->dtd.pool);
  200.     if (!entity) {
  201.       if (!context->dtd.containsRef || context->dtd.standalone) {
  202.         *badPtr = s;
  203.         return undefinedEntity;
  204.       }
  205.       break;
  206.     }
  207.     if (entity->wfInContent)
  208.       break;
  209.     if (entity->open) {
  210.       *badPtr = s;
  211.       return recursiveEntityRef;
  212.     }
  213.     if (entity->notation) {
  214.       *badPtr = s;
  215.       return binaryEntityRef;
  216.     }
  217.     if (entity) {
  218.       if (entity->textPtr) {
  219.         enum WfCheckResult result;
  220.         const ENCODING *internalEnc = XmlGetInternalEncoding(XML_UTF8_ENCODING);
  221.         entity->open = 1;
  222.         result = checkContent(level, context, internalEnc,
  223.                   entity->textPtr, entity->textPtr + entity->textLen,
  224.                   badPtr);
  225.         entity->open = 0;
  226.         if (result && *badPtr) {
  227.           *badPtr = s;
  228.           return result;
  229.         }
  230.         entity->wfInContent = 1;
  231.       }
  232.     }
  233.     break;
  234.       }
  235.     case XML_TOK_START_TAG_NO_ATTS:
  236.       if (level == context->stackSize) {
  237.     context->startName
  238.       = realloc((void *)context->startName, (context->stackSize *= 2) * sizeof(char *));
  239.     if (!context->startName)
  240.       return noMemory;
  241.       }
  242.       context->startName[level++] = s + enc->minBytesPerChar;
  243.       break;
  244.     case XML_TOK_START_TAG_WITH_ATTS:
  245.       if (level == context->stackSize) {
  246.     context->startName = realloc((void *)context->startName, (context->stackSize *= 2) * sizeof(char *));
  247.     if (!context->startName)
  248.       return noMemory;
  249.       }
  250.       context->startName[level++] = s + enc->minBytesPerChar;
  251.       /* fall through */
  252.     case XML_TOK_EMPTY_ELEMENT_WITH_ATTS:
  253.       {
  254.     int i;
  255.     int n = XmlGetAttributes(enc, s, context->attsSize, context->atts);
  256.     if (n > context->attsSize) {
  257.       context->attsSize = 2*n;
  258.       context->atts = realloc((void *)context->atts, context->attsSize * sizeof(ATTRIBUTE));
  259.       if (!context->atts)
  260.         return noMemory;
  261.       XmlGetAttributes(enc, s, n, context->atts);
  262.     }
  263.     for (i = 0; i < n; i++) {
  264.       if (!context->atts[i].normalized) {
  265.         enum WfCheckResult result
  266.           = checkAttributeValue(&context->dtd, enc,
  267.                       context->atts[i].valuePtr,
  268.                     context->atts[i].valueEnd,
  269.                     badPtr);
  270.         if (result)
  271.           return result;
  272.       }
  273.     }
  274.     if (i > 1) {
  275.       enum WfCheckResult result = checkAttributeUniqueness(context, enc, n, badPtr);
  276.       if (result)
  277.         return result;
  278.     }
  279.       }
  280.       break;
  281.     case XML_TOK_END_TAG:
  282.       if (level == startLevel) {
  283.         *badPtr = s;
  284.         return asyncEntity;
  285.       }
  286.       --level;
  287.       if (!XmlSameName(enc, context->startName[level], s + enc->minBytesPerChar * 2)) {
  288.     *badPtr = s;
  289.     return tagMismatch;
  290.       }
  291.       break;
  292.     case XML_TOK_CHAR_REF:
  293.       if (XmlCharRefNumber(enc, s) < 0) {
  294.     *badPtr = s;
  295.     return badCharRef;
  296.       }
  297.       break;
  298.     case XML_TOK_XML_DECL:
  299.       *badPtr = s;
  300.       return misplacedXmlPi;
  301.     }
  302.     s = next;
  303.     if (level == 0) {
  304.       do {
  305.     tok = XmlPrologTok(enc, s, end, &next);
  306.     switch (tok) {
  307.     case XML_TOK_TRAILING_CR:
  308.     case XML_TOK_NONE:
  309.       return wellFormed;
  310.     case XML_TOK_PROLOG_S:
  311.     case XML_TOK_COMMENT:
  312.     case XML_TOK_PI:
  313.       s = next;
  314.       break;
  315.     default:
  316.       if (tok > 0) {
  317.         *badPtr = s;
  318.         return junkAfterDocElement;
  319.       }
  320.       break;
  321.     }
  322.       } while (tok > 0);
  323.     }
  324.     else
  325.       tok = XmlContentTok(enc, s, end, &next);
  326.   }
  327.   /* not reached */
  328. }
  329.  
  330. static
  331. int attcmp(const void *p1, const void *p2)
  332. {
  333.   const ATTRIBUTE *a1 = p1;
  334.   const ATTRIBUTE *a2 = p2;
  335.   size_t n1 = a1->valuePtr - a1->name;
  336.   size_t n2 = a2->valuePtr - a2->name;
  337.  
  338.   if (n1 == n2) {
  339.     int n = memcmp(a1->name, a2->name, n1);
  340.     if (n)
  341.       return n;
  342.     /* Sort identical attribute names by position, so that we always
  343.        report the first duplicate attribute. */
  344.     if (a1->name < a2->name)
  345.       return -1;
  346.     else if (a1->name > a2->name)
  347.       return 1;
  348.     else
  349.       return 0;
  350.   }
  351.   else if (n1 < n2)
  352.     return -1;
  353.   else
  354.     return 1;
  355. }
  356.  
  357. /* Note that this trashes the attribute values. */
  358.  
  359. static enum WfCheckResult
  360. checkAttributeUniqueness(CONTEXT *context, const ENCODING *enc, int nAtts,
  361.              const char **badPtr)
  362. {
  363. #define QSORT_MIN_ATTS 10
  364.   if (nAtts < QSORT_MIN_ATTS) {
  365.     int i;
  366.     for (i = 1; i < nAtts; i++) {
  367.       int j;
  368.       for (j = 0; j < i; j++) {
  369.     if (XmlSameName(enc, context->atts[i].name, context->atts[j].name)) {
  370.       *badPtr = context->atts[i].name;
  371.         return duplicateAttribute;
  372.     }
  373.       }
  374.     }
  375.   }
  376.   else {
  377.     int i;
  378.     const char *dup = 0;
  379.     /* Store the end of the name in valuePtr */
  380.     for (i = 0; i < nAtts; i++) {
  381.       ATTRIBUTE *a = context->atts + i;
  382.       a->valuePtr = a->name + XmlNameLength(enc, a->name);
  383.     }
  384.     qsort(context->atts, nAtts, sizeof(ATTRIBUTE), attcmp);
  385.     for (i = 1; i < nAtts; i++) {
  386.       ATTRIBUTE *a = context->atts + i;
  387.       if (XmlSameName(enc, a->name, a[-1].name)) {
  388.     if (!dup || a->name < dup)
  389.       dup = a->name;
  390.       }
  391.     }
  392.     if (dup) {
  393.       *badPtr = dup;
  394.       return duplicateAttribute;
  395.     }
  396.   }
  397.   return wellFormed;
  398. }
  399.  
  400. static enum WfCheckResult
  401. checkProlog(DTD *dtd, const char *s, const char *end,
  402.         const char **nextPtr, const ENCODING **enc)
  403. {
  404.   const char *entityNamePtr, *entityNameEnd;
  405.   int entityIsParam;
  406.   PROLOG_STATE state;
  407.   ENTITY *entity;
  408.   INIT_ENCODING initEnc;
  409.   XmlInitEncoding(&initEnc, enc);
  410.   XmlPrologStateInit(&state);
  411.   for (;;) {
  412.     const char *next;
  413.     int tok = XmlPrologTok(*enc, s, end, &next);
  414.     switch (XmlTokenRole(&state, tok, s, next, *enc)) {
  415.     case XML_ROLE_XML_DECL:
  416.       {
  417.     const char *encodingName = 0;
  418.     const ENCODING *encoding = 0;
  419.     const char *version;
  420.     int standalone = -1;
  421.     if (!XmlParseXmlDecl(0,
  422.                  *enc,
  423.                  s,
  424.                  next,
  425.                  nextPtr,
  426.                  &version,
  427.                  &encodingName,
  428.                  &encoding,
  429.                  &standalone))
  430.       return syntaxError;
  431.     if (encoding) {
  432.       if (encoding->minBytesPerChar != (*enc)->minBytesPerChar) {
  433.         *nextPtr = encodingName;
  434.         return incorrectEncoding;
  435.       }
  436.       *enc = encoding;
  437.     }
  438.     else if (encodingName) {
  439.       *nextPtr = encodingName;
  440.       return unknownEncoding;
  441.     }
  442.     if (standalone == 1)
  443.       dtd->standalone = 1;
  444.     break;
  445.       }
  446.     case XML_ROLE_DOCTYPE_SYSTEM_ID:
  447.       dtd->containsRef = 1;
  448.       break;
  449.     case XML_ROLE_DOCTYPE_PUBLIC_ID:
  450.     case XML_ROLE_ENTITY_PUBLIC_ID:
  451.     case XML_ROLE_NOTATION_PUBLIC_ID:
  452.       if (!XmlIsPublicId(*enc, s, next, nextPtr))
  453.     return syntaxError;
  454.       break;
  455.     case XML_ROLE_INSTANCE_START:
  456.       *nextPtr = s;
  457.       return wellFormed;
  458.     case XML_ROLE_DEFAULT_ATTRIBUTE_VALUE:
  459.     case XML_ROLE_FIXED_ATTRIBUTE_VALUE:
  460.       {
  461.     const char *tem = 0;
  462.     enum WfCheckResult result
  463.       = checkAttributeValue(dtd, *enc, s + (*enc)->minBytesPerChar,
  464.                 next - (*enc)->minBytesPerChar,
  465.                 &tem);
  466.     if (result) {
  467.       if (tem)
  468.         *nextPtr = tem;
  469.       return result;
  470.     }
  471.     break;
  472.       }
  473.     case XML_ROLE_ENTITY_VALUE:
  474.       {
  475.     enum WfCheckResult result
  476.       = storeEntity(dtd,
  477.             *enc,
  478.             entityIsParam,
  479.             entityNamePtr,
  480.             entityNameEnd,
  481.             s,
  482.             next,
  483.             nextPtr);
  484.     if (result != wellFormed)
  485.       return result;
  486.       }
  487.       break;
  488.     case XML_ROLE_ENTITY_SYSTEM_ID:
  489.       {
  490.     const char *name = poolStoreString(&dtd->pool, *enc, entityNamePtr, entityNameEnd);
  491.     entity = (ENTITY *)lookup(entityIsParam ? &dtd->paramEntities : &dtd->generalEntities,
  492.                   name, sizeof(ENTITY));
  493.     if (entity->name != name) {
  494.       poolDiscard(&dtd->pool);
  495.       entity = 0;
  496.     }
  497.     else {
  498.       poolFinish(&dtd->pool);
  499.       entity->systemId = poolStoreString(&dtd->pool, *enc,
  500.                          s + (*enc)->minBytesPerChar,
  501.                          next - (*enc)->minBytesPerChar);
  502.       poolFinish(&dtd->pool);
  503.     }
  504.       }
  505.       break;
  506.     case XML_ROLE_PARAM_ENTITY_REF:
  507.       {
  508.     const char *name = poolStoreString(&dtd->pool, *enc,
  509.                        s + (*enc)->minBytesPerChar,
  510.                        next - (*enc)->minBytesPerChar);
  511.     ENTITY *entity = (ENTITY *)lookup(&dtd->paramEntities, name, 0);
  512.     poolDiscard(&dtd->pool);
  513.     if (!entity) {
  514.       if (!dtd->containsRef || dtd->standalone) {
  515.         *nextPtr = s;
  516.         return undefinedEntity;
  517.       }
  518.     }
  519.       }
  520.       break;
  521.     case XML_ROLE_ENTITY_NOTATION_NAME:
  522.       if (entity) {
  523.     entity->notation = poolStoreString(&dtd->pool, *enc, s, next);
  524.     poolFinish(&dtd->pool);
  525.       }
  526.       break;
  527.     case XML_ROLE_GENERAL_ENTITY_NAME:
  528.       entityNamePtr = s;
  529.       entityNameEnd = next;
  530.       entityIsParam = 0;
  531.       break;
  532.     case XML_ROLE_PARAM_ENTITY_NAME:
  533.       entityNamePtr = s;
  534.       entityNameEnd = next;
  535.       entityIsParam = 1;
  536.       break;
  537.     case XML_ROLE_ERROR:
  538.       *nextPtr = s;
  539.       switch (tok) {
  540.       case XML_TOK_PARAM_ENTITY_REF:
  541.     return paramEntityRef;
  542.       case XML_TOK_INVALID:
  543.     *nextPtr = next;
  544.     return invalidToken;
  545.       case XML_TOK_NONE:
  546.     return noElements;
  547.       case XML_TOK_PARTIAL:
  548.     return unclosedToken;
  549.       case XML_TOK_PARTIAL_CHAR:
  550.     return partialChar;
  551.       case XML_TOK_TRAILING_CR:
  552.     *nextPtr = s + (*enc)->minBytesPerChar;
  553.     return noElements;
  554.       case XML_TOK_XML_DECL:
  555.     return misplacedXmlPi;
  556.       default:
  557.     return syntaxError;
  558.       }
  559.     case XML_ROLE_GROUP_OPEN:
  560.       if (state.level >= dtd->groupSize) {
  561.     if (dtd->groupSize)
  562.       dtd->groupConnector = realloc(dtd->groupConnector, dtd->groupSize *= 2);
  563.     else
  564.       dtd->groupConnector = malloc(dtd->groupSize = 32);
  565.     if (!dtd->groupConnector)
  566.       return noMemory;
  567.       }
  568.       dtd->groupConnector[state.level] = 0;
  569.       break;
  570.     case XML_ROLE_GROUP_SEQUENCE:
  571.       if (dtd->groupConnector[state.level] == '|') {
  572.     *nextPtr = s;
  573.     return syntaxError;
  574.       }
  575.       dtd->groupConnector[state.level] = ',';
  576.       break;
  577.     case XML_ROLE_GROUP_CHOICE:
  578.       if (dtd->groupConnector[state.level] == ',') {
  579.     *nextPtr = s;
  580.     return syntaxError;
  581.       }
  582.       dtd->groupConnector[state.level] = '|';
  583.       break;
  584.     case XML_ROLE_NONE:
  585.       if (tok == XML_TOK_PARAM_ENTITY_REF)
  586.     dtd->containsRef = 1;
  587.       break;
  588.     }
  589.     s = next;
  590.   }
  591.   /* not reached */
  592. }
  593.  
  594. static enum WfCheckResult
  595. checkParsedEntities(CONTEXT *context, const char **badPtr)
  596. {
  597.   HASH_TABLE_ITER iter;
  598.   hashTableIterInit(&iter, &context->dtd.generalEntities);
  599.   for (;;) {
  600.     ENTITY *entity = (ENTITY *)hashTableIterNext(&iter);
  601.     if (!entity)
  602.       break;
  603.     if (entity->textPtr && !entity->wfInContent && !entity->magic) {
  604.       enum WfCheckResult result;
  605.       const ENCODING *internalEnc = XmlGetInternalEncoding(XML_UTF8_ENCODING);
  606.       entity->open = 1;
  607.       result = checkContent(1, context, internalEnc,
  608.                 entity->textPtr, entity->textPtr + entity->textLen,
  609.                 badPtr);
  610.       entity->open = 0;
  611.       if (result && *badPtr) {
  612.     *badPtr = entity->docTextPtr;
  613.     return result;
  614.       }
  615.       entity->wfInContent = 1;
  616.     }
  617.   }
  618.   return wellFormed;
  619. }
  620.  
  621. static enum WfCheckResult
  622. checkGeneralTextEntity(CONTEXT *context,
  623.                const char *s, const char *end,
  624.                const char **nextPtr,
  625.                const ENCODING **enc)
  626. {
  627.   INIT_ENCODING initEnc;
  628.   const char *next;
  629.   int tok;
  630.  
  631.   XmlInitEncoding(&initEnc, enc);
  632.   tok = XmlContentTok(*enc, s, end, &next);
  633.  
  634.   if (tok == XML_TOK_BOM) {
  635.     s = next;
  636.     tok = XmlContentTok(*enc, s, end, &next);
  637.   }
  638.   if (tok == XML_TOK_XML_DECL) {
  639.     const char *encodingName = 0;
  640.     const ENCODING *encoding = 0;
  641.     const char *version;
  642.     if (!XmlParseXmlDecl(1,
  643.              *enc,
  644.              s,
  645.              next,
  646.              nextPtr,
  647.              &version,
  648.              &encodingName,
  649.              &encoding,
  650.              0))
  651.       return syntaxError;
  652.     if (encoding) {
  653.       if (encoding->minBytesPerChar != (*enc)->minBytesPerChar) {
  654.     *nextPtr = encodingName;
  655.     return incorrectEncoding;
  656.       }
  657.       *enc = encoding;
  658.     }
  659.     else if (encodingName) {
  660.       *nextPtr = encodingName;
  661.       return unknownEncoding;
  662.     }
  663.     s = next;
  664.   }
  665.   context->dtd.containsRef = 1;
  666.   return checkContent(1, context, *enc, s, end, nextPtr);
  667. }
  668.  
  669. static enum WfCheckResult
  670. checkAttributeValue(DTD *dtd, const ENCODING *enc,
  671.             const char *ptr, const char *end, const char **badPtr)
  672. {
  673.   for (;;) {
  674.     const char *next;
  675.     int tok = XmlAttributeValueTok(enc, ptr, end, &next);
  676.     switch (tok) {
  677.     case XML_TOK_TRAILING_CR:
  678.     case XML_TOK_NONE:
  679.       return wellFormed;
  680.     case XML_TOK_INVALID:
  681.       *badPtr = next;
  682.       return invalidToken;
  683.     case XML_TOK_PARTIAL:
  684.       *badPtr = ptr;
  685.       return invalidToken;
  686.     case XML_TOK_CHAR_REF:
  687.       if (XmlCharRefNumber(enc, ptr) < 0) {
  688.     *badPtr = ptr;
  689.     return badCharRef;
  690.       }
  691.       break;
  692.     case XML_TOK_DATA_CHARS:
  693.     case XML_TOK_DATA_NEWLINE:
  694.       break;
  695.     case XML_TOK_ENTITY_REF:
  696.       {
  697.     const char *name = poolStoreString(&dtd->pool, enc,
  698.                        ptr + enc->minBytesPerChar,
  699.                        next - enc->minBytesPerChar);
  700.     ENTITY *entity = (ENTITY *)lookup(&dtd->generalEntities, name, 0);
  701.     poolDiscard(&dtd->pool);
  702.     if (!entity) {
  703.       if (!dtd->containsRef) {
  704.         *badPtr = ptr;
  705.         return undefinedEntity;
  706.       }
  707.       break;
  708.     }
  709.     if (entity->wfInAttribute)
  710.       break;
  711.     if (entity->open) {
  712.       *badPtr = ptr;
  713.       return recursiveEntityRef;
  714.     }
  715.     if (entity->notation) {
  716.       *badPtr = ptr;
  717.       return binaryEntityRef;
  718.     }
  719.     if (entity) {
  720.       if (entity->textPtr) {
  721.         enum WfCheckResult result;
  722.         const ENCODING *internalEnc = XmlGetInternalEncoding(XML_UTF8_ENCODING);
  723.         const char *textEnd = entity->textPtr + entity->textLen;
  724.         entity->open = 1;
  725.         result = checkAttributeValue(dtd, internalEnc, entity->textPtr, textEnd, badPtr);
  726.         entity->open = 0;
  727.         if (result && *badPtr) {
  728.           *badPtr = ptr;
  729.           return result;
  730.         }
  731.         entity->wfInAttribute = 1;
  732.       }
  733.       else {
  734.         *badPtr = ptr;
  735.         return attributeExternalEntityRef;
  736.       }
  737.     }
  738.     break;
  739.       }
  740.       break;
  741.     default:
  742.       abort();
  743.     }
  744.     ptr = next;
  745.   }
  746.   /* not reached */
  747. }
  748.  
  749. static
  750. void poolInit(STRING_POOL *pool)
  751. {
  752.   pool->blocks = 0;
  753.   pool->start = 0;
  754.   pool->ptr = 0;
  755.   pool->end = 0;
  756. }
  757.  
  758. static
  759. void poolDestroy(STRING_POOL *pool)
  760. {
  761.   BLOCK *p = pool->blocks;
  762.   while (p) {
  763.     BLOCK *tem = p->next;
  764.     free(p);
  765.     p = tem;
  766.   }
  767.   pool->blocks = 0;
  768.   pool->ptr = 0;
  769.   pool->start = 0;
  770.   pool->end = 0;
  771. }
  772.  
  773. static
  774. const char *poolAppend(STRING_POOL *pool, const ENCODING *enc,
  775.                const char *ptr, const char *end)
  776. {
  777.   for (;;) {
  778.     XmlConvert(enc, XML_UTF8_ENCODING, &ptr, end, &(pool->ptr), pool->end);
  779.     if (ptr == end)
  780.       break;
  781.     if (!poolGrow(pool))
  782.       return 0;
  783.   }
  784.   return pool->start;
  785. }
  786.  
  787. static
  788. const char *poolStoreString(STRING_POOL *pool, const ENCODING *enc,
  789.                 const char *ptr, const char *end)
  790. {
  791.   if (!poolAppend(pool, enc, ptr, end))
  792.     return 0;
  793.   if (pool->ptr == pool->end && !poolGrow(pool))
  794.     return 0;
  795.   *(pool->ptr)++ = 0;
  796.   return pool->start;
  797. }
  798.  
  799. static
  800. int poolGrow(STRING_POOL *pool)
  801. {
  802.   if (pool->blocks && pool->start == pool->blocks->s) {
  803.     size_t blockSize = (pool->end - pool->start)*2;
  804.     pool->blocks = realloc(pool->blocks, offsetof(BLOCK, s) + blockSize);
  805.     if (!pool->blocks)
  806.       return 0;
  807.     pool->ptr = pool->blocks->s + (pool->ptr - pool->start);
  808.     pool->start = pool->blocks->s;
  809.     pool->end = pool->start + blockSize;
  810.   }
  811.   else {
  812.     BLOCK *tem;
  813.     size_t blockSize = pool->end - pool->start;
  814.     if (blockSize < INIT_BLOCK_SIZE)
  815.       blockSize = INIT_BLOCK_SIZE;
  816.     else
  817.       blockSize *= 2;
  818.     tem = malloc(offsetof(BLOCK, s) + blockSize);
  819.     if (!tem)
  820.       return 0;
  821.     tem->next = pool->blocks;
  822.     pool->blocks = tem;
  823.     memcpy(tem->s, pool->start, pool->ptr - pool->start);
  824.     pool->ptr = tem->s + (pool->ptr - pool->start);
  825.     pool->start = tem->s;
  826.     pool->end = tem->s + blockSize;
  827.   }
  828.   return 1;
  829. }
  830.  
  831. static int dtdInit(DTD *dtd)
  832. {
  833.   static const char *names[] = { "lt", "amp", "gt", "quot", "apos" };
  834.   static const char chars[] = { '<', '&', '>', '"', '\'' };
  835.   int i;
  836.  
  837.   poolInit(&(dtd->pool));
  838.   hashTableInit(&(dtd->generalEntities));
  839.   for (i = 0; i < 5; i++) {
  840.     ENTITY *entity = (ENTITY *)lookup(&(dtd->generalEntities), names[i], sizeof(ENTITY));
  841.     if (!entity)
  842.       return 0;
  843.     entity->textPtr = chars + i;
  844.     entity->textLen = 1;
  845.     entity->magic = 1;
  846.     entity->wfInContent = 1;
  847.     entity->wfInAttribute = 1;
  848.   }
  849.   hashTableInit(&(dtd->paramEntities));
  850.   dtd->containsRef = 0;
  851.   dtd->groupSize = 0;
  852.   dtd->groupConnector = 0;
  853.   return 1;
  854. }
  855.  
  856. static void dtdDestroy(DTD *dtd)
  857. {
  858.   poolDestroy(&(dtd->pool));
  859.   hashTableDestroy(&(dtd->generalEntities));
  860.   hashTableDestroy(&(dtd->paramEntities));
  861.   free(dtd->groupConnector);
  862. }
  863.  
  864. static
  865. enum WfCheckResult storeEntity(DTD *dtd,
  866.                    const ENCODING *enc,
  867.                    int isParam,
  868.                    const char *entityNamePtr,
  869.                    const char *entityNameEnd,
  870.                    const char *entityTextPtr,
  871.                    const char *entityTextEnd,
  872.                    const char **badPtr)
  873. {
  874.   ENTITY *entity;
  875.   const ENCODING *utf8 = XmlGetInternalEncoding(XML_UTF8_ENCODING);
  876.   STRING_POOL *pool = &(dtd->pool);
  877.   if (!poolStoreString(pool, enc, entityNamePtr, entityNameEnd))
  878.     return noMemory;
  879.   entity = (ENTITY *)lookup(isParam ? &(dtd->paramEntities) : &(dtd->generalEntities),
  880.                 pool->start,
  881.                 sizeof(ENTITY));
  882.   if (entity->name != pool->start) {
  883.     poolDiscard(pool);
  884.     entityNamePtr = 0;
  885.   }
  886.   else
  887.     poolFinish(pool);
  888.   entityTextPtr += enc->minBytesPerChar;
  889.   entityTextEnd -= enc->minBytesPerChar;
  890.   entity->docTextPtr = entityTextPtr;
  891.   for (;;) {
  892.     const char *next;
  893.     int tok = XmlEntityValueTok(enc, entityTextPtr, entityTextEnd, &next);
  894.     switch (tok) {
  895.     case XML_TOK_PARAM_ENTITY_REF:
  896.       *badPtr = entityTextPtr;
  897.       return syntaxError;
  898.     case XML_TOK_NONE:
  899.       if (entityNamePtr) {
  900.     entity->textPtr = pool->start;
  901.     entity->textLen = pool->ptr - pool->start;
  902.     poolFinish(pool);
  903.       }
  904.       else
  905.     poolDiscard(pool);
  906.       return wellFormed;
  907.     case XML_TOK_ENTITY_REF:
  908.     case XML_TOK_DATA_CHARS:
  909.       if (!poolAppend(pool, enc, entityTextPtr, next))
  910.     return noMemory;
  911.       break;
  912.     case XML_TOK_TRAILING_CR:
  913.       next = entityTextPtr + enc->minBytesPerChar;
  914.       /* fall through */
  915.     case XML_TOK_DATA_NEWLINE:
  916.       if (pool->end == pool->ptr && !poolGrow(pool))
  917.     return noMemory;
  918.       *(pool->ptr)++ = '\n';
  919.       break;
  920.     case XML_TOK_CHAR_REF:
  921.       {
  922.     char buf[XML_MAX_BYTES_PER_CHAR];
  923.     int i;
  924.     int n = XmlCharRefNumber(enc, entityTextPtr);
  925.     if (n < 0) {
  926.       *badPtr = entityTextPtr;
  927.       return badCharRef;
  928.     }
  929.     n = XmlEncode(utf8, n, buf);
  930.     if (!n) {
  931.       *badPtr = entityTextPtr;
  932.       return badCharRef;
  933.     }
  934.     for (i = 0; i < n; i++) {
  935.       if (pool->end == pool->ptr && !poolGrow(pool))
  936.         return noMemory;
  937.       *(pool->ptr)++ = buf[i];
  938.     }
  939.       }
  940.       break;
  941.     case XML_TOK_PARTIAL:
  942.       *badPtr = entityTextPtr;
  943.       return invalidToken;
  944.     case XML_TOK_INVALID:
  945.       *badPtr = next;
  946.       return invalidToken;
  947.     default:
  948.       abort();
  949.     }
  950.     entityTextPtr = next;
  951.   }
  952.   /* not reached */
  953. }
  954.